﻿using System;
using System.Collections.Generic;
using System.Text;
using UpdateBot2.DownloadSource;
using HtmlAgilityPack;
using System.IO;

namespace UpdateBot2.FileHippo
{
    class FileHippoServer : DownloadSource.DownloadServer
    {

        private FileHippoWebClient _hippoClient { get; set; }

        /// <summary>
        /// Provides public interface to the HippoClient.
        /// </summary>
        public FileHippoWebClient HippoClient
        {
            get
            {
                return this._hippoClient;
            }
        }

        public FileHippoServer(bool noBeta, bool noDemo)
        {
            this._hippoClient = new FileHippoWebClient(noBeta, noDemo);
        }

        public override List<DownloadResourceIdentifier> Search(string programName, int limit)
        {
            throw new NotImplementedException();
        }

        public override DownloadTarget GetDownloadTarget(DownloadResourceIdentifier id)
        {
            HtmlDocument doc = downloadPage(new Uri(id.ID));

            HtmlNode downloadLink = doc.DocumentNode.SelectSingleNode("//div[@id='dlbox']/a");

            //Get latest download link.
            Uri fileHippoBase = new Uri("http://filehippo.com");
            Uri downloadLandingPageURL = new Uri(fileHippoBase, downloadLink.GetAttributeValue("href", string.Empty));
            Uri directFileLink = new Uri(fileHippoBase, ParseDownloadPage(downloadLandingPageURL));

            //This extracts the URL of the program's icon.
            HtmlNode imgElement = doc.DocumentNode.SelectSingleNode("//div[@id='progdesc']/table/tr/td/img");
            String iconURL = imgElement.GetAttributeValue("src", String.Empty);

            //Scan details table.
            String title = String.Empty;
            String filename = String.Empty;
            String publisher = String.Empty;
            String md5 = String.Empty;
            double size = 0;

            //This is the table containing the Technical tab details.
            HtmlNode detailsTable = doc.DocumentNode.SelectSingleNode("//div[@id='progdesc']/div[@id='txt']/table");

            //Iterate through each table row and switch case on each key name.
            foreach (HtmlNode tableRow in detailsTable.ChildNodes)
            {
                String keyName = tableRow.SelectSingleNode("./td[1]").InnerText.Trim();
                String keyValue = tableRow.SelectSingleNode("./td[2]").InnerText.Trim();

                switch (keyName.ToLower())
                {
                    case "title:":
                        title = keyValue;
                        break;
                    case "filename:":
                        filename = keyValue;
                        break;
                    case "file size:":
                        size = parseSize(keyValue);
                        break;
                    case "author:":
                        //Author needs more work. Since its just the text element, grab the first one!
                        //Grab first child of the second table cell.
                        publisher = tableRow.SelectSingleNode("./td[2]/text()").InnerText.Trim();
                        break;
                    case "md5 checksum:":
                        md5 = keyValue;
                        break;
                }
            }

            FileHippoDownload download = new FileHippoDownload(title, new Uri(iconURL), publisher, directFileLink, filename, size, md5);
            download.Version = Version.ExtractVersionFromString(title); //Attempt to extract version from title.
            return download;
        }

        public List<DownloadResourceIdentifier> ParseUpdateCheckerPage(Uri url)
        {
            List<DownloadResourceIdentifier> resourceIDS = new List<DownloadResourceIdentifier>();

            HtmlDocument doc = downloadPage(url);

            //Once more a messy parsing with multiple child nodes; but it works!
            HtmlNode tableElement = doc.DocumentNode.SelectSingleNode("//div[@id='content-2col']/div/div/table");

            foreach (HtmlNode tableRow in tableElement.SelectNodes("./tr"))
            {
                try
                {
                    HtmlNode programLink = tableRow.SelectSingleNode("./td[@class='txt']/h2/a");
                    String programPageURL = programLink.GetAttributeValue("href", String.Empty).Trim();

                    if (!String.IsNullOrEmpty(programPageURL))
                    {
                        Uri techUri = getTechnicalDetailsUri(programPageURL);
                        resourceIDS.Add(new DownloadResourceIdentifier(techUri.ToString()));
                    }
                }
                catch (Exception err)
                {
                    continue;
                }
            }

            return resourceIDS;
        }


        /// <summary>
        /// Downloads the file asyncrounously but waits until complete.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="dir"></param>
        public override void DownloadTargetAsync(DownloadTarget d, DirectoryInfo dir)
        {
            this._hippoClient.DownloadFileAsync(d.DownloadURI, Path.Combine(dir.FullName, d.Filename), d);
        }

        #region Instance Utility Functions

        public String ParseDownloadPage(Uri pageURL)
        {
            String html = this._hippoClient.DownloadString(pageURL);
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);

            HtmlNode downloadLink = doc.GetElementbyId("_ctl0_contentMain_lnkURL");
            return downloadLink.GetAttributeValue("href", String.Empty);
        }

        /// <summary>
        /// Downloads the page and returns it as a HtmlDocument.
        /// Reduces some redundancy.
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private HtmlDocument downloadPage(Uri url)
        {
            String html = this._hippoClient.DownloadString(url);

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            return doc;
        }

        #endregion

        #region Static Utility Functions

        /// <summary>
        /// Parses the following format for file size: 25.38MB (26,610,560 bytes)
        /// Returns double representing number of bytes.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static double parseSize(String s)
        {
            //25.38MB (26,610,560 bytes)
            int firstParaPos = s.IndexOf('(');
            int lastParaPos = s.LastIndexOf(" bytes");
            int subLength = lastParaPos - firstParaPos;

            //Substring based on parentheses...
            String numText = s.Substring(firstParaPos + 1, subLength);
            double returnVal = 0;

            //Remove comas and try to parse it. Returns 0 if it fails.
            double.TryParse(numText.Replace(",", ""), out returnVal);
            return returnVal;
        }

        /// <summary>
        /// Gets the Technical Details URL for the given URL if possible.
        /// </summary>
        /// <param name="hippoPage"></param>
        /// <returns></returns>
        private static Uri getTechnicalDetailsUri(String hippoURL)
        {
            //Get local path...
            Uri hippoPage = new Uri(hippoURL);
            String localPath = hippoPage.LocalPath.TrimStart('/');
            int firstSlash = localPath.IndexOf('/');
            if (firstSlash > -1)
            {
                //There is more info than just program name; cut off anything past first
                //slash and append /tech/ to get technical details page.
                String justProgram = localPath.Substring(0, firstSlash);
                return new Uri("http://filehippo.com/" + justProgram + "/tech/");
            }
            else
            {
                //No slash
                return new Uri(hippoPage, "/tech/");
            }
        }

        #endregion
    }
}